package entility;


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.lang.Comparable;
import java.util.List;

public class Machine  implements Serializable, Comparable<Machine>  {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public String ip;
	public Integer port;
	private String type;
	public static Boolean acceptor;
	public static Integer Times = 0;



	public static Socket  connection;

	/**Generical Machine*/
	public Machine(String ip, Integer port)
	{
		this.ip = ip;
		this.port = port;
	}


	/**Init Machine by Url */
	public Machine(String url)
	{
		String[] mac = url.split(":");
		this.ip = mac[0];
		this.port = Integer.parseInt(mac[1]);

	}

	/**Especific Machine*/
	public Machine(String ip, Integer port, String type)
	{
		this.ip = ip;
		this.port = port;
	}

	public String toSend()
	{
		return this.ip + ":"+this.port;
	}

	@Override
	public String toString() {
		return "\nMachine [ip=" + ip + ", port=" + port + "]";
	}


	public void connect(Integer timeout) throws IOException
	{


		SocketAddress sockaddr = new InetSocketAddress(ip, port);

		if(timeout < 1000)
			timeout = 20000;
		connection = new Socket();
		connection.connect(sockaddr, timeout);

		acceptor = connection.isConnected()&& connection.isClosed();


	}



	public void connect() throws UnknownHostException, IOException
	{
		connection = new Socket(this.ip, this.port);
		acceptor = connection.isConnected()&& !connection.isClosed();


	}

	public List<Machine> getSubServers(String str,String origin) throws Exception
	{

		Job jcl = new JobImpl(origin);
		jcl.setMethod(str);
		Job response = this.getMessage(jcl);
		List<String> data = response.getData();
		List<Machine> servers =  new ArrayList<Machine>();
		for(String server: data)
		{
			String result = server.toString();
			servers.add(new Machine(result.split(":")[0], Integer.parseInt(result.split(":")[1])));
		}
		return servers;

	}


	public Job getMessage(Job message) throws Exception
	{

		try{


			connect();
			ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(connection.getOutputStream()));
			out.writeObject(message);
			out.flush();
			ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(connection.getInputStream()));
			Job response = (JobImpl) in.readObject();
			in.close();
			out.close();		
			connection.close();
			return response;
		}catch(SocketTimeoutException e)
		{
			throw new ConnectionFailException(this.ip);

		}

	}

	public boolean hostAvailabilityCheck() throws UnknownHostException, IOException
	{ 
		Socket  s = new Socket(this.ip, this.port);

		boolean available = true; 
		try {       

			if (s.isConnected())
			{
				s.close();    
			}               
		} 
		catch (UnknownHostException e) 
		{ // unknown host 
			available = false;
			s = null;
		} 
		catch (IOException e) { // io exception, service probably not running 
			available = false;
			s = null;
		} 
		catch (NullPointerException e) {
			available = false;
			s=null;
		}
		return available;   
	} 
	public Job getMessage(Job message, Integer timeout) throws IOException, ClassNotFoundException
	{
		try{

			

			connect(timeout);
			ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(connection.getOutputStream()));
			out.writeObject(message);
			out.flush();
			ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(connection.getInputStream()));
			Job response = (JobImpl) in.readObject();
			in.close();
			out.close();		
			connection.close();
			return response;

		}catch(SocketTimeoutException e)
		{
			throw new ConnectionFailException(this.ip);
		}

	}

	public void close()
	{
		try {
			connection.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}



	public void notifyErrorMachine(Machine machine, String origin) throws Exception
	{

		Notification notify = new Notification();


		notify.setMachine(machine);
		notify.setMessage(Notification.MESSAGEERROR);
		notify.setType(this.getType());


		Job machineFail = new JobImpl(origin);
		machineFail.setMethod("notify");
		machineFail.setData(notify.getJobData());

		this.getMessage(machineFail);

	}






	@Override
	public int compareTo(Machine o) {
		if(this.ip.equals(o.ip))
			return 0;
		else
			return 1;


	}




	public String getType() {
		return type;
	}




	public void setType(String type) {
		this.type = type;
	}
}

